## Built starting from a python version of example code posted on the Ogre3d wiki. As per the wiki's licensing terms,
## I believe this code to be public domain.
## Original C++ version: http://www.ogre3d.org/tikiwiki/Managing+Game+States+with+OGRE

import ogre.renderer.OGRE as ogre
import ogre.gui.CEGUI as CEGUI
import ogre.io.OIS as OIS

class Singleton(type):
    def __init__(cls,name,bases,dic):
        super(Singleton,cls).__init__(name,bases,dic)
        cls.instance=None

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance=super(Singleton,cls).__call__(*args,**kw)
        return cls.instance

class GameState(object):
    def __init__(self):
        pass

    def enter(self):
        pass

    def exit(self):
        pass

    def pause(self):
        pass

    def resume(self):
        pass

    def keyPressed(self, evt):
        pass

    def keyReleased(self, evt):
        pass

    def mousePressed(self, evt, id):
        return True

    def mouseReleased(self, evt, id):
        return True

    def mouseMoved(self, evt):
        return True

    def frameStarted(self, evt):
        return True

    def frameEnded(self, evt):
        return True

    def changeState(self):
        pass

    def pushState(self):
        pass

    def popState(self):
        pass

class InputManager(OIS.KeyListener, OIS.MouseListener):

    def __init__(self, manager, rwindow):
        OIS.KeyListener.__init__(self)
        OIS.MouseListener.__init__(self)

        windowHandle = rwindow.getCustomAttributeInt("WINDOW")
        paramList = [("WINDOW", str(windowHandle))]
        inputManager = OIS.createPythonInputSystem(paramList)

        self.keyboard = inputManager \
                             .createInputObjectKeyboard(OIS.OISKeyboard, True)

        self.mouse = inputManager \
                             .createInputObjectMouse(OIS.OISMouse, True)


        self.keyboard.setEventCallback(self)
        self.mouse.setEventCallback(self)

        self.manager = manager

    def keyPressed(self, evt):
        self.manager.keyPressed(evt)

    def keyReleased(self, evt):
        self.manager.keyReleased(evt)

    def mousePressed(self, evt, id):
        self.manager.mousePressed(evt,id)

    def mouseReleased(self, evt, id):
        self.manager.mouseReleased(evt,id)

    def mouseMoved(self, evt):
        self.manager.mouseMoved(evt)

class GameFrameListener(ogre.FrameListener):

    def __init__(self, gameManager):
        ogre.FrameListener.__init__(self)
        self.gameManager = gameManager

    def frameStarted(self, evt):
        successfulFrame = self.gameManager.frameStarted(evt)
        return successfulFrame

    def frameEnded(self, evt):
        successfulFrame = self.gameManager.frameEnded(evt)
        return successfulFrame

class GameManager(object):

    __metaclass__ = Singleton

    def __init__(self):
        self.root = 0
        self.inputManager = 0
        self.states = []

    def start(self, state):
        self.root = ogre.Root()

        self.defineResources()
        self.setupRenderSystem()

        self.frameListener = GameFrameListener(self)
        self.root.addFrameListener(self.frameListener)

        self.inputManager = InputManager(self, self.RenderWindow)

        self.changeState(state())
        self.root.startRendering()

    def changeState(self, state):
        if len(self.states):
            oldState = self.states.pop()
            oldState.exit()

        self.states.append(state)
        state.enter()

    def pushState(self, state):
        if len(self.states):
            self.states[-1].pause()

        self.states.append(state)
        state.enter()

    def popState(self):
        if len(self.states):
            oldState = self.states.pop()
            oldState.exit()

        if len(self.states):
            self.states[-1].resume()

    def keyPressed(self, evt):
        self.states[-1].keyPressed(evt)

    def keyReleased(self, evt):
        self.states[-1].keyReleased(evt)

    def mousePressed(self, evt, id):
        self.states[-1].mousePressed(evt,id)

    def mouseReleased(self, evt, id):
        self.states[-1].mouseReleased(evt,id)

    def mouseMoved(self, evt):
        self.states[-1].mouseMoved(evt)

    def frameStarted(self, evt):
        self.inputManager.keyboard.capture()
        self.inputManager.mouse.capture()
        sf = self.states[-1].frameStarted(evt)
        return sf

    def frameEnded(self, evt):
        sf = self.states[-1].frameEnded(evt)
        return sf

    def setupRenderSystem(self):
        if not self.root.restoreConfig() and not self.root.showConfigDialog():
            raise Exception("User cancelled config dialogue")

        self.RenderWindow = self.root.initialise(True, "GameWindow")
        ogre.ResourceGroupManager.getSingleton().initialiseAllResourceGroups()

    def defineResources(self):
        conf = ogre.ConfigFile()
        conf.load("resources.cfg")
        seci = conf.getSectionIterator()

        while seci.hasMoreElements():
            sectionName = seci.peekNextKey()
            settings = seci.getNext()

            for item in settings:
                typeName = item.key
                archName = item.value
                ogre.ResourceGroupManager.getSingleton().addResourceLocation \
                 (archName, typeName, sectionName)